<!DOCTYPE html>
<html lang="zh-CN">





<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/blog/img/favicon.png">
  <link rel="icon" type="image/png" href="/blog/img/favicon.png">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
  
  <meta name="theme-color" content="#35495e">
  <meta name="description" content="小菜头的博客，主要记录学习笔记，包含：html,css,javascript,jQuery,vue等">
  <meta name="author" content="A-cai">
  <meta name="keywords" content="html,css,javascript,jQuery,vue">
  <title>js执行顺序机制 - A-cai的blog</title>

  <link  rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.staticfile.org/github-markdown-css/4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/blog/lib/hint/hint.min.css" />

  
    <link  rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.0.0/styles/atom-one-dark.min.css" />
  

  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_yg9cfy8wd6.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_pjno9b9zyxs.css">


<link  rel="stylesheet" href="/blog/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script  src="/blog/js/utils.js" ></script>
<meta name="generator" content="Hexo 4.2.0"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/blog/">&nbsp;<strong>A-cai的学习笔记</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/blog/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner intro-2" id="background" parallax=true
         style="background: url('/blog/img/default.png') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="container text-center white-text fade-in-up">
            <span class="h2" id="subtitle">
              
            </span>

            
              
  <div class="mt-3 post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2020-06-26 14:05">
      2020年6月26日 下午
    </time>
  </div>


<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      3.4k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      39
       分钟
    </span>
  

  
  
    
      <!-- 不蒜子统计文章PV -->
      <span id="busuanzi_container_page_pv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="busuanzi_value_page_pv"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid">
  <div class="row">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-md">
      <div class="container nopadding-md" id="board-ctn">
        <div class="py-5" id="board">
          <div class="post-content mx-auto" id="post">
            
              <p class="note note-info">
                
                  本文最后更新于：2020年3月23日 凌晨
                
              </p>
            
            <article class="markdown-body">
              <h1 id="弄懂-JavaScript-执行机制"><a href="#弄懂-JavaScript-执行机制" class="headerlink" title="弄懂 JavaScript 执行机制"></a>弄懂 JavaScript 执行机制</h1><p>本文的目的就是要保证你彻底弄懂javascript的执行机制，如果读完本文还不懂，可以揍我。</p>
<p>不论你是javascript新手还是老鸟，不论是面试求职，还是日常开发工作，我们经常会遇到这样的情况：给定的几行代码，我们需要知道其输出内容和顺序。因为javascript是一门单线程语言，所以我们可以得出结论：</p>
<ul>
<li>javascript是按照语句出现的顺序执行的</li>
</ul>
<p>看到这里读者要打人了：我难道不知道js是一行一行执行的？还用你说？稍安勿躁，正因为js是一行一行执行的，所以我们以为js都是这样的：</p>
<div class="hljs"><pre><code class="hljs javascript"><span class="hljs-keyword">let</span> a = <span class="hljs-string">'1'</span>;
<span class="hljs-built_in">console</span>.log(a);

<span class="hljs-keyword">let</span> b = <span class="hljs-string">'2'</span>;
<span class="hljs-built_in">console</span>.log(b);复制代码</code></pre></div>



<p><img src="https://user-gold-cdn.xitu.io/2017/11/20/15fd87f7221d0dbe?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" srcset="/blog/img/loading.gif" alt="img"></p>
<p>然而实际上js是这样的：</p>
<div class="hljs"><pre><code class="hljs matlab">setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span>&#123;</span>
    console.<span class="hljs-built_in">log</span>(<span class="hljs-string">'定时器开始啦'</span>)
&#125;);

new Promise(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(resolve)</span>&#123;</span>
    console.<span class="hljs-built_in">log</span>(<span class="hljs-string">'马上执行for循环啦'</span>);
    <span class="hljs-keyword">for</span>(var <span class="hljs-built_in">i</span> = <span class="hljs-number">0</span>; <span class="hljs-built_in">i</span> &lt; <span class="hljs-number">10000</span>; <span class="hljs-built_in">i</span>++)&#123;
        <span class="hljs-built_in">i</span> == <span class="hljs-number">99</span> &amp;&amp; resolve();
    &#125;
&#125;).then(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span>&#123;</span>
    console.<span class="hljs-built_in">log</span>(<span class="hljs-string">'执行then函数啦'</span>)
&#125;);

console.<span class="hljs-built_in">log</span>(<span class="hljs-string">'代码执行结束'</span>);复制代码</code></pre></div>



<p><img src="https://user-gold-cdn.xitu.io/2017/11/20/15fd87d38acc4905?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" srcset="/blog/img/loading.gif" alt="img"></p>
<p>依照<strong>js是按照语句出现的顺序执行</strong>这个理念，我自信的写下输出结果：</p>
<div class="hljs"><pre><code class="hljs awk"><span class="hljs-regexp">//</span><span class="hljs-string">"定时器开始啦"</span>
<span class="hljs-regexp">//</span><span class="hljs-string">"马上执行for循环啦"</span>
<span class="hljs-regexp">//</span><span class="hljs-string">"执行then函数啦"</span>
<span class="hljs-regexp">//</span><span class="hljs-string">"代码执行结束"</span>复制代码</code></pre></div>

<p>去chrome上验证下，结果完全不对，瞬间懵了，说好的一行一行执行的呢？</p>
<p><img src="https://user-gold-cdn.xitu.io/2017/11/20/15fd8840f3c3f109?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" srcset="/blog/img/loading.gif" alt="img"></p>
<p>我们真的要彻底弄明白javascript的执行机制了。</p>
<h3 id="1-关于javascript"><a href="#1-关于javascript" class="headerlink" title="1.关于javascript"></a>1.关于javascript</h3><p>javascript是一门<strong>单线程</strong>语言，在最新的HTML5中提出了Web-Worker，但javascript是单线程这一核心仍未改变。所以一切javascript版的”多线程”都是用单线程模拟出来的，一切javascript多线程都是纸老虎！</p>
<h3 id="2-javascript事件循环"><a href="#2-javascript事件循环" class="headerlink" title="2.javascript事件循环"></a>2.javascript事件循环</h3><p>既然js是单线程，那就像只有一个窗口的银行，客户需要排队一个一个办理业务，同理js任务也要一个一个顺序执行。如果一个任务耗时过长，那么后一个任务也必须等着。那么问题来了，假如我们想浏览新闻，但是新闻包含的超清图片加载很慢，难道我们的网页要一直卡着直到图片完全显示出来？因此聪明的程序员将任务分为两类：</p>
<ul>
<li>同步任务</li>
<li>异步任务</li>
</ul>
<p>当我们打开网站时，网页的渲染过程就是一大堆同步任务，比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务，就是异步任务。关于这部分有严格的文字定义，但本文的目的是用最小的学习成本彻底弄懂执行机制，所以我们用导图来说明：</p>
<p><img src="https://user-gold-cdn.xitu.io/2017/11/21/15fdd88994142347?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" srcset="/blog/img/loading.gif" alt="img"></p>
<p>导图要表达的内容用文字来表述的话：</p>
<ul>
<li>同步和异步任务分别进入不同的执行”场所”，同步的进入主线程，异步的进入Event Table并注册函数。</li>
<li>当指定的事情完成时，Event Table会将这个函数移入Event Queue。</li>
<li>主线程内的任务执行完毕为空，会去Event Queue读取对应的函数，进入主线程执行。</li>
<li>上述过程会不断重复，也就是常说的Event Loop(事件循环)。</li>
</ul>
<p>我们不禁要问了，那怎么知道主线程执行栈为空啊？js引擎存在monitoring process进程，会持续不断的检查主线程执行栈是否为空，一旦为空，就会去Event Queue那里检查是否有等待被调用的函数。</p>
<p>说了这么多文字，不如直接一段代码更直白：</p>
<div class="hljs"><pre><code class="hljs javascript"><span class="hljs-keyword">let</span> data = [];
$.ajax(&#123;
    url:www.javascript.com,
    data:data,
    success:<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> &#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'发送成功!'</span>);
    &#125;
&#125;)
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'代码执行结束'</span>);复制代码</code></pre></div>

<p>上面是一段简易的<code>ajax</code>请求代码：</p>
<ul>
<li>ajax进入Event Table，注册回调函数<code>success</code>。</li>
<li>执行<code>console.log(&#39;代码执行结束&#39;)</code>。</li>
<li>ajax事件完成，回调函数<code>success</code>进入Event Queue。</li>
<li>主线程从Event Queue读取回调函数<code>success</code>并执行。</li>
</ul>
<p>相信通过上面的文字和代码，你已经对js的执行顺序有了初步了解。接下来我们来研究进阶话题：setTimeout。</p>
<h3 id="3-又爱又恨的setTimeout"><a href="#3-又爱又恨的setTimeout" class="headerlink" title="3.又爱又恨的setTimeout"></a>3.又爱又恨的setTimeout</h3><p>大名鼎鼎的<code>setTimeout</code>无需再多言，大家对他的第一印象就是异步可以延时执行，我们经常这么实现延时3秒执行：</p>
<div class="hljs"><pre><code class="hljs coffeescript">setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> &#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'延时3秒'</span>);
&#125;,<span class="hljs-number">3000</span>)复制代码</code></pre></div>

<p>渐渐的<code>setTimeout</code>用的地方多了，问题也出现了，有时候明明写的延时3秒，实际却5，6秒才执行函数，这又咋回事啊？</p>
<p>先看一个例子：</p>
<div class="hljs"><pre><code class="hljs coffeescript">setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> &#123;
    task();
&#125;,<span class="hljs-number">3000</span>)
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'执行console'</span>);复制代码</code></pre></div>

<p>根据前面我们的结论，<code>setTimeout</code>是异步的，应该先执行<code>console.log</code>这个同步任务，所以我们的结论是：</p>
<div class="hljs"><pre><code class="hljs 1c"><span class="hljs-comment">//执行console</span>
<span class="hljs-comment">//task()复制代码</span></code></pre></div>

<p>去验证一下，结果正确！<br>然后我们修改一下前面的代码：</p>
<div class="hljs"><pre><code class="hljs stylus"><span class="hljs-function"><span class="hljs-title">setTimeout</span><span class="hljs-params">(()</span></span> =&gt; &#123;
    task()
&#125;,<span class="hljs-number">3000</span>)

<span class="hljs-function"><span class="hljs-title">sleep</span><span class="hljs-params">(<span class="hljs-number">10000000</span>)</span></span>复制代码</code></pre></div>

<p>乍一看其实差不多嘛，但我们把这段代码在chrome执行一下，却发现控制台执行<code>task()</code>需要的时间远远超过3秒，说好的延时三秒，为啥现在需要这么长时间啊？</p>
<p>这时候我们需要重新理解<code>setTimeout</code>的定义。我们先说上述代码是怎么执行的：</p>
<ul>
<li><code>task()</code>进入Event Table并注册,计时开始。</li>
<li>执行<code>sleep</code>函数，很慢，非常慢，计时仍在继续。</li>
<li>3秒到了，计时事件<code>timeout</code>完成，<code>task()</code>进入Event Queue，但是<code>sleep</code>也太慢了吧，还没执行完，只好等着。</li>
<li><code>sleep</code>终于执行完了，<code>task()</code>终于从Event Queue进入了主线程执行。</li>
</ul>
<p>上述的流程走完，我们知道<code>setTimeout</code>这个函数，是经过指定时间后，把要执行的任务(本例中为<code>task()</code>)加入到Event Queue中，又因为是单线程任务要一个一个执行，如果前面的任务需要的时间太久，那么只能等着，导致真正的延迟时间远远大于3秒。</p>
<p>我们还经常遇到<code>setTimeout(fn,0)</code>这样的代码，0秒后执行又是什么意思呢？是不是可以立即执行呢？</p>
<p>答案是不会的，<code>setTimeout(fn,0)</code>的含义是，指定某个任务在主线程最早可得的空闲时间执行，意思就是不用再等多少秒了，只要主线程执行栈内的同步任务全部执行完成，栈为空就马上执行。举例说明：</p>
<div class="hljs"><pre><code class="hljs coffeescript"><span class="hljs-regexp">//</span>代码<span class="hljs-number">1</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'先执行这里'</span>);
setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> &#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'执行啦'</span>)
&#125;,<span class="hljs-number">0</span>);复制代码
<span class="hljs-regexp">//</span>代码<span class="hljs-number">2</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'先执行这里'</span>);
setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> &#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'执行啦'</span>)
&#125;,<span class="hljs-number">3000</span>);复制代码</code></pre></div>

<p>代码1的输出结果是：</p>
<div class="hljs"><pre><code class="hljs 1c"><span class="hljs-comment">//先执行这里</span>
<span class="hljs-comment">//执行啦复制代码</span></code></pre></div>

<p>代码2的输出结果是：</p>
<div class="hljs"><pre><code class="hljs jboss-cli"><span class="hljs-string">//</span>先执行这里
<span class="hljs-string">//</span> <span class="hljs-string">...</span> 3s later
<span class="hljs-string">//</span> 执行啦复制代码</code></pre></div>

<p>关于<code>setTimeout</code>要补充的是，即便主线程为空，0毫秒实际上也是达不到的。根据HTML的标准，最低是4毫秒。有兴趣的同学可以自行了解。</p>
<h3 id="4-又恨又爱的setInterval"><a href="#4-又恨又爱的setInterval" class="headerlink" title="4.又恨又爱的setInterval"></a>4.又恨又爱的setInterval</h3><p>上面说完了<code>setTimeout</code>，当然不能错过它的孪生兄弟<code>setInterval</code>。他俩差不多，只不过后者是循环的执行。对于执行顺序来说，<code>setInterval</code>会每隔指定的时间将注册的函数置入Event Queue，如果前面的任务耗时太久，那么同样需要等待。</p>
<p>唯一需要注意的一点是，对于<code>setInterval(fn,ms)</code>来说，我们已经知道不是每过<code>ms</code>秒会执行一次<code>fn</code>，而是每过<code>ms</code>秒，会有<code>fn</code>进入Event Queue。一旦<strong><code>setInterval</code>的回调函数<code>fn</code>执行时间超过了延迟时间<code>ms</code>，那么就完全看不出来有时间间隔了</strong>。这句话请读者仔细品味。</p>
<h3 id="5-Promise与process-nextTick-callback"><a href="#5-Promise与process-nextTick-callback" class="headerlink" title="5.Promise与process.nextTick(callback)"></a>5.Promise与process.nextTick(callback)</h3><p>传统的定时器我们已经研究过了，接着我们探究<code>Promise</code>与<code>process.nextTick(callback)</code>的表现。</p>
<p><code>Promise</code>的定义和功能本文不再赘述，不了解的读者可以学习一下阮一峰老师的<a href="http://es6.ruanyifeng.com/#docs/promise" target="_blank" rel="noopener">Promise</a>。而<code>process.nextTick(callback)</code>类似node.js版的”setTimeout”，在事件循环的下一次循环中调用 callback 回调函数。</p>
<p>我们进入正题，除了广义的同步任务和异步任务，我们对任务有更精细的定义：</p>
<ul>
<li>macro-task(宏任务)：包括整体代码script，setTimeout，setInterval</li>
<li>micro-task(微任务)：Promise，process.nextTick</li>
</ul>
<p>不同类型的任务会进入对应的Event Queue，比如<code>setTimeout</code>和<code>setInterval</code>会进入相同的Event Queue。</p>
<p>事件循环的顺序，决定js代码的执行顺序。进入整体代码(宏任务)后，开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始，找到其中一个任务队列执行完毕，再执行所有的微任务。听起来有点绕，我们用文章最开始的一段代码说明：</p>
<div class="hljs"><pre><code class="hljs javascript">setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'setTimeout'</span>);
&#125;)

<span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">resolve</span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'promise'</span>);
&#125;).then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'then'</span>);
&#125;)

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'console'</span>);复制代码</code></pre></div>

<ul>
<li>这段代码作为宏任务，进入主线程。</li>
<li>先遇到<code>setTimeout</code>，那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同，下文不再描述)</li>
<li>接下来遇到了<code>Promise</code>，<code>new Promise</code>立即执行，<code>then</code>函数分发到微任务Event Queue。</li>
<li>遇到<code>console.log()</code>，立即执行。</li>
<li>好啦，整体代码script作为第一个宏任务执行结束，看看有哪些微任务？我们发现了<code>then</code>在微任务Event Queue里面，执行。</li>
<li>ok，第一轮事件循环结束了，我们开始第二轮循环，当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中<code>setTimeout</code>对应的回调函数，立即执行。</li>
<li>结束。</li>
</ul>
<p>事件循环，宏任务，微任务的关系如图所示：</p>
<p><img src="https://user-gold-cdn.xitu.io/2017/11/21/15fdcea13361a1ec?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" srcset="/blog/img/loading.gif" alt="img"></p>
<p>我们来分析一段较复杂的代码，看看你是否真的掌握了js的执行机制：</p>
<div class="hljs"><pre><code class="hljs javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'1'</span>);

setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'2'</span>);
    process.nextTick(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'3'</span>);
    &#125;)
    <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">resolve</span>) </span>&#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'4'</span>);
        resolve();
    &#125;).then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'5'</span>)
    &#125;)
&#125;)
process.nextTick(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'6'</span>);
&#125;)
<span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">resolve</span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'7'</span>);
    resolve();
&#125;).then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'8'</span>)
&#125;)

setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'9'</span>);
    process.nextTick(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'10'</span>);
    &#125;)
    <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">resolve</span>) </span>&#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'11'</span>);
        resolve();
    &#125;).then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'12'</span>)
    &#125;)
&#125;)复制代码</code></pre></div>

<p>第一轮事件循环流程分析如下：</p>
<ul>
<li>整体script作为第一个宏任务进入主线程，遇到<code>console.log</code>，输出1。</li>
<li>遇到<code>setTimeout</code>，其回调函数被分发到宏任务Event Queue中。我们暂且记为<code>setTimeout1</code>。</li>
<li>遇到<code>process.nextTick()</code>，其回调函数被分发到微任务Event Queue中。我们记为<code>process1</code>。</li>
<li>遇到<code>Promise</code>，<code>new Promise</code>直接执行，输出7。<code>then</code>被分发到微任务Event Queue中。我们记为<code>then1</code>。</li>
<li>又遇到了<code>setTimeout</code>，其回调函数被分发到宏任务Event Queue中，我们记为<code>setTimeout2</code>。</li>
</ul>
<table>
<thead>
<tr>
<th align="center">宏任务Event Queue</th>
<th align="center">微任务Event Queue</th>
</tr>
</thead>
<tbody><tr>
<td align="center">setTimeout1</td>
<td align="center">process1</td>
</tr>
<tr>
<td align="center">setTimeout2</td>
<td align="center">then1</td>
</tr>
</tbody></table>
<ul>
<li>上表是第一轮事件循环宏任务结束时各Event Queue的情况，此时已经输出了1和7。</li>
<li>我们发现了<code>process1</code>和<code>then1</code>两个微任务。</li>
<li>执行<code>process1</code>,输出6。</li>
<li>执行<code>then1</code>，输出8。</li>
</ul>
<p>好了，第一轮事件循环正式结束，这一轮的结果是输出1，7，6，8。那么第二轮时间循环从<code>setTimeout1</code>宏任务开始：</p>
<ul>
<li>首先输出2。接下来遇到了<code>process.nextTick()</code>，同样将其分发到微任务Event Queue中，记为<code>process2</code>。<code>new Promise</code>立即执行输出4，<code>then</code>也分发到微任务Event Queue中，记为<code>then2</code>。</li>
</ul>
<table>
<thead>
<tr>
<th align="center">宏任务Event Queue</th>
<th align="center">微任务Event Queue</th>
</tr>
</thead>
<tbody><tr>
<td align="center">setTimeout2</td>
<td align="center">process2</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">then2</td>
</tr>
</tbody></table>
<ul>
<li>第二轮事件循环宏任务结束，我们发现有<code>process2</code>和<code>then2</code>两个微任务可以执行。</li>
<li>输出3。</li>
<li>输出5。</li>
<li>第二轮事件循环结束，第二轮输出2，4，3，5。</li>
<li>第三轮事件循环开始，此时只剩setTimeout2了，执行。</li>
<li>直接输出9。</li>
<li>将<code>process.nextTick()</code>分发到微任务Event Queue中。记为<code>process3</code>。</li>
<li>直接执行<code>new Promise</code>，输出11。</li>
<li>将<code>then</code>分发到微任务Event Queue中，记为<code>then3</code>。</li>
</ul>
<table>
<thead>
<tr>
<th align="center">宏任务Event Queue</th>
<th align="center">微任务Event Queue</th>
</tr>
</thead>
<tbody><tr>
<td align="center"></td>
<td align="center">process3</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">then3</td>
</tr>
</tbody></table>
<ul>
<li>第三轮事件循环宏任务执行结束，执行两个微任务<code>process3</code>和<code>then3</code>。</li>
<li>输出10。</li>
<li>输出12。</li>
<li>第三轮事件循环结束，第三轮输出9，11，10，12。</li>
</ul>
<p>整段代码，共进行了三次事件循环，完整的输出为1，7，6，8，2，4，3，5，9，11，10，12。<br>(请注意，node环境下的事件监听依赖libuv与前端环境不完全相同，输出顺序可能会有误差)</p>
<h3 id="6-写在最后"><a href="#6-写在最后" class="headerlink" title="6.写在最后"></a>6.写在最后</h3><h4 id="1-js的异步"><a href="#1-js的异步" class="headerlink" title="(1)js的异步"></a>(1)js的异步</h4><p>我们从最开头就说javascript是一门单线程语言，不管是什么新框架新语法糖实现的所谓异步，其实都是用同步的方法去模拟的，牢牢把握住单线程这点非常重要。</p>
<h4 id="2-事件循环Event-Loop"><a href="#2-事件循环Event-Loop" class="headerlink" title="(2)事件循环Event Loop"></a>(2)事件循环Event Loop</h4><p>事件循环是js实现异步的一种方法，也是js的执行机制。</p>
<h4 id="3-javascript的执行和运行"><a href="#3-javascript的执行和运行" class="headerlink" title="(3)javascript的执行和运行"></a>(3)javascript的执行和运行</h4><p>执行和运行有很大的区别，javascript在不同的环境下，比如node，浏览器，Ringo等等，执行方式是不同的。而运行大多指javascript解析引擎，是统一的。</p>
<h4 id="4-setImmediate"><a href="#4-setImmediate" class="headerlink" title="(4)setImmediate"></a>(4)setImmediate</h4><p>微任务和宏任务还有很多种类，比如<code>setImmediate</code>等等，执行都是有共同点的，有兴趣的同学可以自行了解。</p>
<h4 id="5-最后的最后"><a href="#5-最后的最后" class="headerlink" title="(5)最后的最后"></a>(5)最后的最后</h4><ul>
<li>javascript是一门单线程语言</li>
<li>Event Loop是javascript的执行机制</li>
</ul>
<p>牢牢把握两个基本点，以认真学习javascript为中心，早日实现成为前端高手的伟大梦想！</p>
<p><img src="https://user-gold-cdn.xitu.io/2017/11/21/15fdd96beade6575?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" srcset="/blog/img/loading.gif" alt="img"></p>

            </article>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                
              </div>
              
                <p class="note note-warning">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" target="_blank" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！</p>
              
              
                <div class="post-prevnext row">
                  <div class="post-prev col-6">
                    
                    
                      <a href="/blog/2020/06/26/%E5%BC%82%E6%AD%A5%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E5%92%8C%E5%8F%91%E5%B1%95%E5%8E%86%E7%A8%8B/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">异步解决方案和发展历程</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </div>
                  <div class="post-next col-6">
                    
                    
                      <a href="/blog/2020/06/26/%E5%89%8D%E7%AB%AF%E5%8A%A0%E5%AF%86%E7%9A%84%E5%B8%B8%E8%A7%81%E5%9C%BA%E6%99%AF%E5%92%8C%E6%96%B9%E6%B3%95/">
                        <span class="hidden-mobile">前端加密的常见场景和方法</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </div>
                </div>
              
            </div>

            
              <!-- Comments -->
              <div class="comments" id="comments">
                
                

              </div>
            
          </div>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div id="tocbot"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    
  </main>

  
    <a id="scroll-top-button" href="#" role="button">
      <i class="iconfont icon-arrowup" aria-hidden="true"></i>
    </a>
  

  
    <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
  

  

  

  <footer class="mt-5">
  <div class="text-center py-3">
    <div>
      <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
      <i class="iconfont icon-love"></i>
      <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener">
        <span>Fluid</span></a>
    </div>
    

    

    
  </div>
</footer>

<!-- SCRIPTS -->
<script  src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js" ></script>
<script  src="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/js/bootstrap.min.js" ></script>
<script  src="/blog/js/debouncer.js" ></script>
<script  src="/blog/js/main.js" ></script>

<!-- Plugins -->


  
    <script  src="/blog/js/lazyload.js" ></script>
  



  <script defer src="https://cdn.staticfile.org/clipboard.js/2.0.6/clipboard.min.js" ></script>
  <script  src="/blog/js/clipboard-use.js" ></script>



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





  <script  src="https://cdn.staticfile.org/tocbot/4.11.1/tocbot.min.js" ></script>
  <script>
    $(document).ready(function () {
      var boardCtn = $('#board-ctn');
      var boardTop = boardCtn.offset().top;

      tocbot.init({
        tocSelector: '#tocbot',
        contentSelector: 'article.markdown-body',
        headingSelector: 'h1,h2,h3,h4,h5,h6',
        linkClass: 'tocbot-link',
        activeLinkClass: 'tocbot-active-link',
        listClass: 'tocbot-list',
        isCollapsedClass: 'tocbot-is-collapsed',
        collapsibleClass: 'tocbot-is-collapsible',
        collapseDepth: 0,
        scrollSmooth: true,
        headingsOffset: -boardTop
      });
      if ($('.toc-list-item').length > 0) {
        $('#toc').css('visibility', 'visible');
      }
    });
  </script>



  <script  src="https://cdn.staticfile.org/typed.js/2.0.11/typed.min.js" ></script>
  <script>
    var typed = new Typed('#subtitle', {
      strings: [
        '  ',
        "js执行顺序机制&nbsp;",
      ],
      cursorChar: "|",
      typeSpeed: 70,
      loop: true,
    });
    typed.stop();
    $(document).ready(function () {
      $(".typed-cursor").addClass("h2");
      typed.start();
    });
  </script>



  <script  src="https://cdn.staticfile.org/anchor-js/4.2.2/anchor.min.js" ></script>
  <script>
    anchors.options = {
      placement: "right",
      visible: "hover",
      
    };
    var el = "h1,h2,h3,h4,h5,h6".split(",");
    var res = [];
    for (item of el) {
      res.push(".markdown-body > " + item)
    }
    anchors.add(res.join(", "))
  </script>



  <script  src="/blog/js/local-search.js" ></script>
  <script>
    var path = "/blog/local-search.xml";
    var inputArea = document.querySelector("#local-search-input");
    inputArea.onclick = function () {
      searchFunc(path, 'local-search-input', 'local-search-result');
      this.onclick = null
    }
  </script>



  <script  src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css" />

  <script>
    $('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
      function () {
        var element = document.createElement('a');
        $(element).attr('data-fancybox', 'images');
        $(element).attr('href', $(this).attr('src'));
        $(this).wrap(element);
      }
    );
  </script>







  
  
    <script>
      !function (e, t, a) {
        function r() {
          for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x + "px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e].scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
          requestAnimationFrame(r)
        }

        function n() {
          var t = "function" == typeof e.onclick && e.onclick;
          e.onclick = function (e) {
            t && t(), o(e)
          }
        }

        function o(e) {
          var a = t.createElement("div");
          a.className = "heart", s.push({
            el: a,
            x: e.clientX - 5,
            y: e.clientY - 5,
            scale: 1,
            alpha: 1,
            color: c()
          }), t.body.appendChild(a)
        }

        function i(e) {
          var a = t.createElement("style");
          a.type = "text/css";
          try {
            a.appendChild(t.createTextNode(e))
          } catch (t) {
            a.styleSheet.cssText = e
          }
          t.getElementsByTagName("head")[0].appendChild(a)
        }

        function c() {
          return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + ")"
        }

        var s = [];
        e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {
          setTimeout(e, 1e3 / 60)
        }, i(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"), n(), r()
      }(window, document);
    </script>
  













</body>
</html>
